/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.editpart.connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.NodeEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.uml2.uml.Element;

import com.cea.papyrus.core.figures.ElementFigure;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Root class of all ConnectionEditPart.
 * This class contains utility methods and provide basic management of anchors.
 * 
 * @author dumoulin
 */
public abstract class AnchorableGraphEdgeEditPart extends AbstractConnectionEditPart implements AnchorableEditPart, NodeEditPart {
	
	/**
	 * associated anchor manager.
	 */
	private AnchorMngr anchorMngr;
//	private ConnectorAnchorsMngrEditPart bugAnchorMngr;

	/**
 * 
 */
protected Listener modelChangelistener;
	
	/**
	 * The model associated to this element is a GraphEdge.
	 * 
	 * @return the GraphEdge
	 */
	public GraphEdge getGraphicalModel() {
		return (GraphEdge)getModel();
	}
	
	/**
	 * Called when the model is changed.
	 * 
	 * @param notification 
	 */
	public void notifyChanged(Notification notification) {
//		How can we differenciate change kind ?
		Object feature = notification.getFeature();
		if (feature != null && feature instanceof EReference) {
			EReference reference = (EReference) feature;
			if ("anchorage".equals(reference.getName())) {
				refreshSourceConnections();
				refreshTargetConnections();
			} 
			else if ("waypoints".equals(reference.getName()))
			{
				refreshSourceConnections();
				refreshTargetConnections();
			} else
				refreshVisuals();
		} else
			refreshVisuals();
	}
	
	/**
	 * Called when the EditPart is activated. Usually, we need to listen to
	 * model changes.
	 */
	public void activate() {
		if (isActive())
			return;
//		start listening for changes in the model
		((Notifier) getGraphicalModel()).eAdapters().add(getModelAdapter());
		super.activate();
	}
	
	/**
	 * Called when the EditPart is deactivated. Usually, we need to remove
	 * listening to model changes.
	 */
	public void deactivate() {
		if (!isActive())
			return;
//		stop listening for changes in the model
		((Notifier) getGraphicalModel()).eAdapters().remove(getModelAdapter());
		super.deactivate();
	}
	
	/**
	 * 
	 * 
	 * @return 
	 */
	protected Adapter getModelAdapter() {
		if (modelChangelistener == null)
			modelChangelistener = new Listener();
		return modelChangelistener;
	}
	
	/**
	 * Returns the Figure of this, as a node type figure.
	 * The default figure is a elemt node : a red rectangle
	 * 
	 * @return  Figure as a NodeFigure.
	 */
	protected ElementFigure getNodeFigure(){
		return (ElementFigure)this.createFigure() ;
	}
	
	/**
	 * Get the Bussiness Model. Throw NullPointerException if no such element.
	 * 
	 * @return 
	 * 
	 * @throws NullPointerException 
	 */
	protected Element getBusinessModel() throws NullPointerException
	{
		return  ((Uml1SemanticModelBridge)getGraphicalModel().getSemanticModel()).getElement();
	}
	
	/**
	 * Install EditPolicies.
	 * Here, install the anchorManager.
	 */
	protected void createEditPolicies() {
		anchorMngr = getAnchorMngr();
	}
	
	/**
	 * Get the anchor mngr. Create it if necessary.
	 * 
	 * @return 
	 */
	public AnchorMngr getAnchorMngr() {
		if(anchorMngr == null)
			anchorMngr = createAnchorManager();
		return anchorMngr;
	}
	
	/**
	 * Create the AnchorMngr.
	 * Subclasses should override this method to provide appropriate anchors manager.
	 * 
	 * @return 
	 */
	protected AnchorMngr createAnchorManager() {
		return new PolylineAnchorMngr(this);
	}
	
	/**
	 * 
	 * 
	 * @param connection 
	 * 
	 * @return 
	 */
	public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection) {
		return getAnchorMngr().getSourceConnectionAnchor(connection);
	}
	
	/**
	 * 
	 * 
	 * @param request 
	 * 
	 * @return 
	 */
	public ConnectionAnchor getSourceConnectionAnchor(Request request) {
		return getAnchorMngr().getSourceConnectionAnchor(request);
	}
	
	/**
	 * 
	 * 
	 * @param connection 
	 * 
	 * @return 
	 */
	public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection) {
		return getAnchorMngr().getTargetConnectionAnchor(connection);
	}
	
	/**
	 * 
	 * 
	 * @param request 
	 * 
	 * @return 
	 */
	public ConnectionAnchor getTargetConnectionAnchor(Request request) {
		return getAnchorMngr().getTargetConnectionAnchor(request);
	}
	/*
	 * 
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	protected List getModelSourceConnections() {
//		TODO Replace the next one by a view list (no list creation)
		List<GraphEdge> res = new ArrayList<GraphEdge>();
		Iterator<GraphConnector> anchors = getGraphicalModel().getAnchorage().iterator();
		while(anchors.hasNext())
		{
			GraphConnector anchor = anchors.next();
			Iterator<GraphEdge> edges = anchor.getGraphEdge().iterator();
			while(edges.hasNext())
			{
				GraphEdge edge = edges.next();
//				Check if this is a source anchor.
				if( edge.getAnchor().get(0)== anchor)
					res.add(edge);
			}
		}
		return res;
	}
	/*
	 * 
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	protected List getModelTargetConnections() {
//		TODO Replace the next one by a view list (no list creation)
		List<GraphEdge> res = new ArrayList<GraphEdge>();
		Iterator<GraphConnector> anchors = getGraphicalModel().getAnchorage().iterator();
		while(anchors.hasNext())
		{
			GraphConnector anchor = anchors.next();
			Iterator<GraphEdge> edges = anchor.getGraphEdge().iterator();
			while(edges.hasNext())
			{
				GraphEdge edge = edges.next();
//				Check if this is a source anchor.
				if( edge.getAnchor().get(1)== anchor)
					res.add(edge);
			}
		}
		return res;
	}
	
	/**
	 * Nested class used as Adapter. The adapter is delagated here because of a
	 * name clash with getTarget (from parent and from Adapter).
	 * 
	 * @author dumoulin
	 */
	protected class Listener implements Adapter {
		
		/**
		 * needed for emf Adapter.
		 */
		private Notifier adapterTarget;
		
		/**
		 * Called when the model is changed. Make it abstract ?
		 * 
		 * @param notification 
		 */
		public void notifyChanged(Notification notification) {
			AnchorableGraphEdgeEditPart.this.notifyChanged(notification);
		}
		
		/**
		 * 
		 * 
		 * @param type 
		 * 
		 * @return 
		 */
		public boolean isAdapterForType(Object type) {
			return type.equals(getBusinessModel().getClass());
		}
		
		/**
		 * 
		 * 
		 * @return 
		 */
		public Notifier getTarget() {
			return adapterTarget;
		}
		
		/**
		 * 
		 * 
		 * @param newTarget 
		 */
		public void setTarget(Notifier newTarget) {
			adapterTarget = newTarget;
		}
	} // end inner class
}
